/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.common.util;

import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

/**
 * 时间处理工具类
 *
 * @author vacoor
 */
public abstract class Dates {
    /**
     * One second: 1000 milliseconds.
     */
    public static final long SECOND = 1000l;

    /**
     * One minute: 60 seconds (60 000 milliseconds).
     */
    public static final long MINUTE = 60l * SECOND;

    /**
     * One hour: 60 minutes (3 600 000 milliseconds).
     * 60 minutes = 3 600 seconds = 3 600 000 milliseconds
     */
    public static final long HOUR = 60l * MINUTE;

    /**
     * One day: 24 hours (86 400 000 milliseconds).
     * 24 hours = 1 440 minutes = 86 400 seconds = 86 400 000 milliseconds.
     */
    public static final long DAY = 24l * HOUR;

    /**
     * One calendar year: 365.2425 days (31556952000 milliseconds).
     * 365.2425 days = 8765.82 hours = 525949.2 minutes = 31556952 seconds
     * = 31556952000 milliseconds.
     */
    public static final long CALENDAR_YEAR = 3652425L * 24L * 60L * 6L;

    /**
     * 获取当前系统时间
     */
    public static Date now() {
        return new Date();
    }

    /**
     * 获取当前系统时间的毫秒数
     */
    public static long currentTimeMillis() {
        return System.currentTimeMillis();
    }

    /**
     * 获取当前系统时间的秒的毫秒数
     */
    public static long currentTimeSecond() {
        return roundToSecond(System.currentTimeMillis());
    }

    /**
     * 获取当前系统时间的分钟的毫秒数
     */
    public static long currentTimeMinute() {
        return roundToMinute(System.currentTimeMillis());
    }

    /**
     * 获取当前系统时间的小时的毫秒数
     */
    public static long currentTimeHour() {
        return roundToHour(System.currentTimeMillis());
    }

    /**
     * 获取当前系统时间的天的毫秒数
     */
    public static long currentTimeDay() {
        return roundToDay(System.currentTimeMillis());
    }

    /**
     * 获取当前系统时间的毫秒数相对于给定基准毫秒数的增量
     *
     * @param mills 基准时间
     */
    public static long delta(long mills) {
        return currentTimeMillis() - mills;
    }

    /**
     * 获取当前系统时间相对于给定基准时间的增量
     *
     * @param date 基准时间
     */
    public static long delta(Date date) {
        return currentTimeMillis() - date.getTime();
    }

    /**
     * 获取给定时间毫秒数对应的秒时间的毫秒数
     *
     * @param time time 时间毫秒数
     */
    public static long roundToSecond(long time) {
        return (time / SECOND) * SECOND;
    }

    /**
     * 获取给定时间毫秒数对应的秒时间的毫秒数
     *
     * @param date 时间
     */
    public static Date roundToSecond(Date date) {
        return new Date(roundToSecond(date.getTime()));
    }

    /**
     * 获取给定时间毫秒数所在的分钟时间毫秒数
     *
     * @param time 时间毫秒数
     */
    public static long roundToMinute(long time) {
        return (time / MINUTE) * MINUTE;
    }

    /**
     * 获取给定时间所在的分钟时间
     *
     * @param date 时间
     */
    public static Date roundToMinute(Date date) {
        return new Date(roundToMinute(date.getTime()));
    }

    /**
     * 获取给定时间毫秒数所对应的小时的毫秒数
     *
     * @param time 时间毫秒数
     */
    public static long roundToHour(long time) {
        // TODO: What if timezone offset is sub hour? Are there any? I think so...
        return ((time / HOUR) * HOUR);
    }

    /**
     * 获取给定时间所在的小时时间
     *
     * @param date 时间
     */
    public static Date roundToHour(Date date) {
        return new Date(roundToHour(date.getTime()));
    }

    /**
     * 获取给定时间毫秒数所在天的时间毫秒数, 使用默认时区
     *
     * @param time 时间毫秒数
     */
    public static long roundToDay(long time) {
        return roundToDay(time, TimeZone.getDefault());
    }

    /**
     * 获取给定时间毫秒数所在天的毫秒数
     *
     * @param time     时间毫秒数
     * @param timeZone 时区
     */
    public static long roundToDay(long time, TimeZone timeZone) {
        int offset = timeZone.getOffset(time);
        return (((time + offset) / DAY) * DAY) - offset;
    }

    /**
     * 获取给定时间所在天的时间, 使用默认时区
     *
     * @param date 时间
     */
    public static Date roundToDay(Date date) {
        return roundToDay(date, TimeZone.getDefault());
    }

    /**
     * 获取给定时间所在天的时间
     *
     * @param date     时间
     * @param timeZone 时区
     */
    public static Date roundToDay(Date date, TimeZone timeZone) {
        return new Date(roundToDay(date.getTime(), timeZone));
    }

    /**
     * 获取当前系统时间 millis 毫秒之前的时间
     *
     * @param millis 毫秒数
     */
    public static Date before(long millis) {
        return before(millis, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取当前系统时间 duration * unit 之前的时间
     *
     * @param duration 时间间隔
     * @param unit     时间单位
     */
    public static Date before(long duration, TimeUnit unit) {
        return before(now(), duration, unit);
    }

    /**
     * 获取给定时间 millis 毫秒前的时间
     *
     * @param date   基准时间
     * @param millis 毫秒数
     */
    public static Date before(Date date, long millis) {
        return before(date, millis, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取给定时间 duration * unit 之前的时间
     *
     * @param date     基准时间
     * @param duration 时间间隔
     * @param unit     时间单位
     */
    public static Date before(Date date, long duration, TimeUnit unit) {
        return new Date(date.getTime() - unit.toMillis(duration));
    }

    /**
     * 获取当前系统时间之后 millis 毫秒后的时间
     *
     * @param millis 毫秒数
     */
    public static Date after(long millis) {
        return after(millis, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取当前系统时间 duration * unit 后的时间
     *
     * @param duration 时间间隔
     * @param unit     时间单位
     */
    public static Date after(long duration, TimeUnit unit) {
        return before(now(), duration, unit);
    }

    /**
     * 获取给定时间 millis 毫秒后的时间
     *
     * @param date   基准时间
     * @param millis 毫秒数
     */
    public static Date after(Date date, long millis) {
        return after(date, millis, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取给定时间 duration * unit 后的时间
     *
     * @param date     基准时间
     * @param duration 时间间隔
     * @param unit     时间单位
     */
    public static Date after(Date date, long duration, TimeUnit unit) {
        return new Date(date.getTime() + unit.toMillis(duration));
    }

    private Dates() {
    }
}

